using BasicFramework.Helper;
using SlimDX;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using BasicFramework.Services.MeshLoader.Nodes;

namespace BasicFramework.Services.MeshLoader.Meshes
{
    public class ColladaModel : IDisposable
    {
        public const string ColladaExtension = "DAE";

        public string name;

        public Dictionary<string, string> images = new Dictionary<string, string>();
        public List<MaterialNode> materials = new List<MaterialNode>();
        public List<EffectNode> EffectNodes = new List<EffectNode>();

        public List<MeshContainer> meshes = new List<MeshContainer>();
        public List<SceneNode> SceneNodes = new List<SceneNode>();
        public List<Bone> bones = new List<Bone>(); //List of bones, first bone = root bone

        int numOfAnimations = 1; // Number of values in the animationMatrices in each bone.
        float frameRate = 30;

        #region Constructor
        public ColladaModel(string filename)
        {
            using (Stream file = System.IO.File.OpenRead(filename))
            {
                string colladaXml = new StreamReader(file).ReadToEnd();
                XmlNode colladaFile = XmlHelper.LoadXmlFromText(colladaXml);

                LoadScene(colladaFile);
                LoadMaterial(colladaFile);
                LoadBones(colladaFile);
                CalculateAbsoluteBoneMatrices();
                LoadMesh(colladaFile);
                // Load controller node ???
                LoadAnimation(colladaFile);
            }
        } // ColladaModel(setFilename)
        #endregion

        #region Dispose
        public void Dispose() { }
        #endregion

        #region Matrix helper methods

        //This function "repairs" the world matrix to compensate for the fact that Blender uses a different axis system
        private Matrix FixMatrix(Matrix m)
        {

            Vector3 VTranslate;
            Vector3 VScale;
            Vector3 VRotate;
            Quaternion QRotate;

            //Decompose matrix into it's components
            m.Decompose(out VScale, out QRotate, out VTranslate);

            VRotate.X = QRotate.X;
            VRotate.Y = QRotate.Z; //swap components
            VRotate.Z = QRotate.Y;

            //return axis swapped matrix
            return Matrix.Scaling(VScale) * Matrix.RotationAxis(VRotate, -QRotate.Angle) * Matrix.Translation(VTranslate);
        }

        private static Matrix LoadColladaMatrix(float[] mat, int offset)
        {
            Matrix m = new Matrix();

            // Put array of floats into matrix.
            for (int i = 0; i <= 15; i++)
            {
                //m[i/4, i%4] = mat[i]; //Rowbased vs columnbased
                m[i % 4, i / 4] = mat[i];
            }
            return m;
        }
        #endregion

        #region Load materials, images and effects
        /// <summary>
        /// Load material information from a collada file
        /// </summary>
        /// <param name="colladaFile">Collada file xml node</param>
        private void LoadMaterial(XmlNode colladaFile)
        {
            System.Console.Out.WriteLine("*Begin Loadmaterial*");

            #region library-images
            //////////////////////////////////////
            ////Library-images
            //////////////////////////////////////

            // First get all textures, if there are none, ignore and quit.
            XmlNode texturesNode = XmlHelper.GetChildNode(colladaFile, "library_images");
            if (texturesNode == null)
                return;

            // Get all used texture images
            foreach (XmlNode textureNode in texturesNode)
            {
                if (textureNode.Name == "image")
                {
                    string filename = StringHelper.ExtractFilename(XmlHelper.GetChildNode(textureNode, "init_from").InnerText, false);
                    string imgname = XmlHelper.GetXmlAttribute(textureNode, "name");

                    //Add to images dictionary if it's not allready in the dictionary, and the filename is filled in
                    if (!(images.ContainsKey(imgname)) && (filename.Length != 0))
                    {
                        images.Add(imgname, filename);
                        System.Console.Out.WriteLine("Texture '" + imgname + "'found: " + filename);
                    }
                }
                else
                {
                    //throw new InvalidOperationException("Unknown Node " + textureNode.Name + " found in library_images");
                }
            }
            #endregion

            #region library-effects
            //////////////////////////////////////
            ////Library-effects
            //////////////////////////////////////

            // Load all effects to find out the used effect in our material
            XmlNode effectsNode = XmlHelper.GetChildNode(colladaFile, "library_effects");
            if (effectsNode == null)
                throw new InvalidOperationException("library_effects node not found while loading Collada file " + name);

            foreach (XmlNode effectNode in effectsNode)
            {
                XmlNode effect = XmlHelper.GetChildNode(effectNode, "init_from");

                EffectNode e = new EffectNode();
                e.Name = XmlHelper.GetXmlAttribute(effectNode, "name");

                if (effect != null)
                    e.InitFrom = effect.InnerText;

                //if there's a texture, there's no diffuse color
                XmlNode DiffuseNode = XmlHelper.GetChildNode(XmlHelper.GetChildNode(effectNode, "diffuse"), "color");
                if (DiffuseNode != null)
                {
                    float[] Diffuse = StringHelper.ConvertStringToFloatArray(DiffuseNode.InnerText);
                    e.FDiffuse = new Vector4(Diffuse[0], Diffuse[1], Diffuse[2], Diffuse[3]);
                }

                XmlNode AmbientNode = XmlHelper.GetChildNode(effectNode, "ambient");
                if (AmbientNode != null)
                {
                    XmlNode ColorNode = XmlHelper.GetChildNode(AmbientNode, "color");
                    float[] Ambient = StringHelper.ConvertStringToFloatArray(ColorNode.InnerText);
                    e.FAmbient = new Vector4(Ambient[0], Ambient[1], Ambient[2], Ambient[3]);
                }

                XmlNode SpecularNode = XmlHelper.GetChildNode(effectNode, "specular");
                if (SpecularNode != null)
                {
                    XmlNode ColorNode = XmlHelper.GetChildNode(SpecularNode, "color");
                    float[] Specular = StringHelper.ConvertStringToFloatArray(ColorNode.InnerText);
                    e.FSpecular = new Vector4(Specular[0], Specular[1], Specular[2], Specular[3]);
                }


                XmlNode ShininessNode = XmlHelper.GetChildNode(effectNode, "shininess");
                if (ShininessNode != null)
                {
                    e.FShininess = Convert.ToSingle(XmlHelper.GetChildNode(ShininessNode, "float").InnerText);
                }

                XmlNode TransparencyNode = XmlHelper.GetChildNode(effectNode, "transparency");
                if (TransparencyNode != null)
                {
                    e.FAlpha = Convert.ToSingle(XmlHelper.GetChildNode(TransparencyNode, "float").InnerText);
                }


                //Add to effects dictionary
                EffectNodes.Add(e);
            }
            #endregion

            #region Library-materials
            //////////////////////////////////////
            ////Library-Materials
            //////////////////////////////////////

            XmlNode materialsNode = XmlHelper.GetChildNode(colladaFile, "library_materials");
            if (materialsNode == null)
                throw new InvalidOperationException("library_materials node not found while loading Collada file " + name);

            foreach (XmlNode materialNode in materialsNode)
            {
                if (materialNode.Name == "material")
                {
                    MaterialNode m = new MaterialNode();
                    m.Name = XmlHelper.GetXmlAttribute(materialNode, "name");
                    m.Effect = XmlHelper.GetXmlAttribute(XmlHelper.GetChildNode(materialNode, "instance_effect"), "url");
                    m.InitFrom = XmlHelper.GetChildNode(XmlHelper.GetChildNode(materialNode, "instance_effect"), "init_from").InnerText;
                    //Add the effect to the list
                    materials.Add(m);
                }
                else
                {
                    throw new InvalidOperationException("Unknown Node " + materialNode.Name + " found in library_materials");
                }
            }
            #endregion

            System.Console.Out.WriteLine("*End Loadmaterial*");
        }
        #endregion

        #region Load bones
        private void LoadBones(XmlNode colladaFile)
        {
            // We need to find the bones in the visual scene
            XmlNode visualSceneNode = XmlHelper.GetChildNode(XmlHelper.GetChildNode(colladaFile, "library_visual_scenes"), "visual_scene");

            FillBoneNodes(null, visualSceneNode);
        }

        private void FillBoneNodes(Bone parentBone, XmlNode boneNodes)
        {
            foreach (XmlNode boneNode in boneNodes)
                if (boneNode.Name == "node" && (XmlHelper.GetXmlAttribute(boneNode, "id").Contains("Bone") || XmlHelper.GetXmlAttribute(boneNode, "type").Contains("JOINT")))
                {
                    Matrix matrix = Matrix.Identity;

                    // Get all sub nodes for the matrix
                    foreach (XmlNode subnode in boneNode)
                    {
                        switch (subnode.Name)
                        {
                            case "translate": System.Console.Out.WriteLine("translate not implemented yet for bone" + bones.Count); break;
                            case "rotate": System.Console.Out.WriteLine("rotate not implemented yet for bone" + bones.Count); break;
                            case "matrix": matrix = FixMatrix(LoadColladaMatrix(StringHelper.ConvertStringToFloatArray(subnode.InnerText), 0));
                                break;
                        }
                    }

                    // Create this node, use the current number of bones as number.
                    Bone newBone = new Bone(matrix, parentBone, bones.Count, XmlHelper.GetXmlAttribute(boneNode, "sid"));

                    // Add to our global bones list
                    bones.Add(newBone);
                    // And to our parent, this way we have a tree and a flat list in
                    // the bones list :)
                    if (parentBone != null) parentBone.children.Add(newBone);

                    // Create all children (will do nothing if there are no sub bones)
                    FillBoneNodes(newBone, boneNode);
                }
        }
        #endregion

        #region Load Scene

        private void LoadScene(XmlNode colladaFile)
        {

            XmlNode scenes = XmlHelper.GetChildNode(colladaFile, "library_visual_scenes");
            if (scenes == null)
                throw new InvalidOperationException("library_visual_scenes node not found in collada file " + name);

            foreach (XmlNode scene in scenes)
            {
                if (scene.Name == "visual_scene")
                {

                    foreach (XmlNode objectnode in scene)
                    {
                        SceneNode sceneNode = new SceneNode();
                        sceneNode.Name = XmlHelper.GetXmlAttribute(objectnode, "name");

                        foreach (XmlNode subnode in objectnode)
                        {

                            switch (subnode.Name)
                            {
                                case "instance_geometry":
                                    sceneNode.Type = SceneNode.SceneNodeType.Geometry;
                                    sceneNode.GeometryLink = XmlHelper.GetXmlAttribute(subnode, "url").Replace("#", "");

                                    XmlNode BindMaterials = XmlHelper.GetChildNode(subnode, "bind_material");
                                    XmlNode Techniques = XmlHelper.GetChildNode(BindMaterials, "technique_common"); // if this line fails, your modellers forgot to texture a mesh !

                                    foreach (XmlNode instancematerial in Techniques)
                                    {
                                        if (instancematerial.Name != "instance_material")
                                            break;
                                        sceneNode.Materials.Add(XmlHelper.GetXmlAttribute(instancematerial, "symbol"));
                                    }
                                    Console.WriteLine("instance geometry");
                                    break;
                                case "instance_camera":
                                    sceneNode.Type = SceneNode.SceneNodeType.Camera;
                                    Console.WriteLine("instance camere");
                                    //make camera entry
                                    break;
                                case "instance_light":
                                    sceneNode.Type = SceneNode.SceneNodeType.Light;
                                    Console.WriteLine("instance light");
                                    //make light entry
                                    break;
                                case "matrix":
                                    sceneNode.World = FixMatrix(LoadColladaMatrix(StringHelper.ConvertStringToFloatArray(subnode.InnerText), 0));
                                    // save position matrix for object
                                    break;

                                default:
                                    Console.WriteLine("Unknow node found, problem !");
                                    break;
                            }

                        }
                        SceneNodes.Add(sceneNode);
                    }
                }
            }
        }

        #endregion

        #region Load mesh
        private void LoadMesh(XmlNode colladaFile)
        {
            XmlNode geometrys = XmlHelper.GetChildNode(colladaFile, "library_geometries");
            if (geometrys == null)
                throw new InvalidOperationException("library_geometries node not found in collada file " + name);

            foreach (XmlNode geometry in geometrys)
            {
                System.Console.WriteLine(geometry.Name);

                if (geometry.Name == "geometry")
                {
                    //Enumerate mesh nodes
                    XmlNode meshnode = XmlHelper.GetChildNode(geometry, "mesh");

                    // Load everything from the mesh node
                    List<MeshContainer> newmeshes = LoadMeshGeometry(colladaFile, meshnode, XmlHelper.GetXmlAttribute(geometry, "id"));
                    foreach (MeshContainer m in newmeshes)
                    {
                        //GenerateVertexAndIndexBuffers(meshes[meshes.Count - 1]);
                        m.GenerateVertexAndIndexBuffers();
                        meshes.Add(m);
                    }

                    // Generate vertex buffer for current mesh node

                }
            }
        }

        /// <summary>
        /// Load mesh geometry
        /// </summary>
        /// <param name="geometry"></param>
        private List<MeshContainer> LoadMeshGeometry(XmlNode colladaFile, XmlNode meshNode, String name)
        {
            List<MeshContainer> newmeshes = new List<MeshContainer>();


            #region Load all source nodes
            Dictionary<string, List<float>> sources = new Dictionary<string, List<float>>();
            Dictionary<string, int> sourcesStride = new Dictionary<string, int>();
            foreach (XmlNode node in meshNode)
            {
                if (node.Name != "source")
                    continue;
                XmlNode floatArray = XmlHelper.GetChildNode(node, "float_array");
                List<float> floats = new List<float>(StringHelper.ConvertStringToFloatArray(floatArray.InnerText));

                // Fill the array up
                int count = Convert.ToInt32(XmlHelper.GetXmlAttribute(floatArray, "count"), NumberFormatInfo.InvariantInfo);
                while (floats.Count < count)
                    floats.Add(0.0f);

                sources.Add(XmlHelper.GetXmlAttribute(node, "id"), floats);

                XmlNode technique = XmlHelper.GetChildNode(node, "technique_common");
                XmlNode acc = XmlHelper.GetChildNode(technique, "accessor");
                String stride = XmlHelper.GetXmlAttribute(acc, "stride");
                sourcesStride.Add(XmlHelper.GetXmlAttribute(node, "id"), int.Parse(stride));
            } // foreach
            #endregion

            #region Vertices
            // Also add vertices node, redirected to position node into sources
            XmlNode verticesNode = XmlHelper.GetChildNode(meshNode, "vertices");
            XmlNode posInput = XmlHelper.GetChildNode(verticesNode, "input");
            if (XmlHelper.GetXmlAttribute(posInput, "semantic").ToLower(CultureInfo.InvariantCulture) != "position")
                throw new InvalidOperationException("unsupported feature found in collada \"vertices\" node");
            string verticesValueName = XmlHelper.GetXmlAttribute(posInput, "source").Substring(1);
            sources.Add(XmlHelper.GetXmlAttribute(verticesNode, "id"), sources[verticesValueName]);
            sourcesStride.Add(XmlHelper.GetXmlAttribute(verticesNode, "id"), sourcesStride[verticesValueName]);
            #endregion

            #region Skincontroller

            XmlNode skincontroller = XmlHelper.GetChildNode(colladaFile, "library_controllers");
            List<Vector3> vertexSkinJoints = new List<Vector3>();
            List<Vector3> vertexSkinWeights = new List<Vector3>();

            if (skincontroller == null)
            {
                System.Console.Out.WriteLine("The model has no skinning data or there was an error finding it");
            }

            else
            {
                System.Console.Out.WriteLine("Loading skinning data");


                #region Load helper matrixes (bind shape and all bind poses matrices)
                // Fixme: multiple skinns: We only support 1 skinning, so just load the first skin node!

                XmlNode skinNode = XmlHelper.GetChildNode(XmlHelper.GetChildNode(XmlHelper.GetChildNode(colladaFile, "library_controllers"), "controller"), "skin");
                //m.objectMatrix = FixMatrix(LoadColladaMatrix(StringHelper.ConvertStringToFloatArray(XmlHelper.GetChildNode(skinNode, "bind_shape_matrix").InnerText), 0));

                // Get the order of the bones used in collada (can be different than ours)
                int[] boneArrayOrder = new int[bones.Count];
                int[] invBoneArrayOrder = new int[bones.Count];
                string boneNameArray = XmlHelper.GetChildNode(skinNode, "Name_array").InnerText;
                int arrayIndex = 0;
                foreach (string boneName in boneNameArray.Split(' '))
                {
                    boneArrayOrder[arrayIndex] = -1;
                    foreach (Bone bone in bones)
                        if (bone.id == boneName)
                        {
                            boneArrayOrder[arrayIndex] = bone.num;
                            invBoneArrayOrder[bone.num] = arrayIndex;
                            break;
                        } // foreach

                    if (boneArrayOrder[arrayIndex] == -1)
                        throw new InvalidOperationException(
                          "Unable to find boneName=" + boneName +
                          " in our bones array for skinning!");
                    arrayIndex++;
                } // foreach
                #endregion

                #region Load weights
                float[] weights = null;
                foreach (XmlNode sourceNode in skinNode)
                {
                    // Get all inv bone skin matrices
                    if (sourceNode.Name == "source" &&
                      XmlHelper.GetXmlAttribute(sourceNode, "id").Contains("bind_poses"))
                    {
                        // Get inner float array
                        float[] mat = StringHelper.ConvertStringToFloatArray(
                          XmlHelper.GetChildNode(sourceNode, "float_array").InnerText);
                        for (int boneNum = 0; boneNum < bones.Count; boneNum++)
                            if (mat.Length / 16 > boneNum)
                            {
                                bones[boneArrayOrder[boneNum]].invBoneSkinMatrix = FixMatrix(LoadColladaMatrix(mat, boneNum * 16));
                            } // for if
                    } // if

                    // Get all weights
                    if (sourceNode.Name == "source" && XmlHelper.GetXmlAttribute(sourceNode, "id").Contains("skin-weights"))
                    {
                        // Get inner float array
                        weights = StringHelper.ConvertStringToFloatArray(XmlHelper.GetChildNode(sourceNode, "float_array").InnerText);
                    } // if
                } // foreach

                if (weights == null)
                    throw new InvalidOperationException("No weights were found in our skin, unable to continue!");
                #endregion

                #region Prepare weights and joint indices, we only want the top 3!
                // Helper to access the bones (first index) and weights (second index).
                // If we got more than 2 indices for an entry here, there are multiple
                // weights used (usually 1 to 3, if more, we only use the strongest).
                XmlNode vertexWeightsNode = XmlHelper.GetChildNode(skinNode, "vertex_weights");
                int[] vcountArray = StringHelper.ConvertStringToIntArray(XmlHelper.GetChildNode(skinNode, "vcount").InnerText);
                int[] vArray = StringHelper.ConvertStringToIntArray(XmlHelper.GetChildNode(skinNode, "v").InnerText);

                // Build vertexSkinJoints and vertexSkinWeights for easier access.
                int vArrayIndex = 0;
                for (int num = 0; num < vcountArray.Length; num++)
                {
                    int vcount = vcountArray[num];
                    List<int> jointIndices = new List<int>();
                    List<int> weightIndices = new List<int>();
                    for (int i = 0; i < vcount; i++)
                    {
                        // Make sure we convert the internal number to our bone numbers!
                        jointIndices.Add(boneArrayOrder[vArray[vArrayIndex]]);
                        weightIndices.Add(vArray[vArrayIndex + 1]);
                        vArrayIndex += 2;
                    } // for

                    // If we got less than 3 values, add until we have enough,
                    // this makes the computation easier below
                    while (jointIndices.Count < 3)
                        jointIndices.Add(0);
                    while (weightIndices.Count < 3)
                        weightIndices.Add(-1);

                    // Find out top 3 weights
                    float[] weightValues = new float[weightIndices.Count];
                    int[] bestWeights = { 0, 1, 2 };
                    for (int i = 0; i < weightIndices.Count; i++)
                    {
                        // Use weight of zero for invalid indices.
                        if (weightIndices[i] < 0 || weightValues[i] >= weights.Length)
                            weightValues[i] = 0;
                        else
                            weightValues[i] = weights[weightIndices[i]];

                        // Got 4 or more weights? Then just select the top 3!
                        if (i >= 3)
                        {
                            float lowestWeight = 1.0f;
                            int lowestWeightOverride = 2;
                            for (int b = 0; b < bestWeights.Length; b++)
                                if (lowestWeight > weightValues[bestWeights[b]])
                                {
                                    lowestWeight = weightValues[bestWeights[b]];
                                    lowestWeightOverride = b;
                                } // for if
                            // Replace lowest weight
                            bestWeights[lowestWeightOverride] = i;
                        } // if
                    } // for

                    // Now build 2 vectors from the best weights
                    Vector3 boneIndicesVec = new Vector3(
                      jointIndices[bestWeights[0]],
                      jointIndices[bestWeights[1]],
                      jointIndices[bestWeights[2]]);
                    Vector3 weightsVec = new Vector3(
                      weightValues[bestWeights[0]],
                      weightValues[bestWeights[1]],
                      weightValues[bestWeights[2]]);
                    // Renormalize weight, important if we got more entries before
                    // and always good to do!
                    float totalWeights = weightsVec.X + weightsVec.Y + weightsVec.Z;
                    if (totalWeights == 0)
                        weightsVec.X = 1.0f;
                    else
                    {
                        weightsVec.X /= totalWeights;
                        weightsVec.Y /= totalWeights;
                        weightsVec.Z /= totalWeights;
                    } // else

                    vertexSkinJoints.Add(boneIndicesVec);
                    vertexSkinWeights.Add(weightsVec);
                } // for
                #endregion
            }

            #endregion

            #region Construct all triangle polygons from the vertex data
            // Construct and generate vertices lists. Every 3 vertices will
            // span one triangle polygon, but everything is optimized later.
            foreach (XmlNode trianglenode in meshNode)
            {
                //Fixme: make meshcontainer here prolly to fix submeshes
                if (trianglenode.Name != "triangles")
                    continue;
                MeshContainer m = new MeshContainer();
                m.Name = name;


                //Fixme: check out this
                string material = XmlHelper.GetXmlAttribute(trianglenode, "material");
                string texturename = "";

                foreach (EffectNode e in EffectNodes)
                {
                    if (e.Name == material + "-fx")
                    {
                        if ((e.InitFrom != "") && (images.ContainsKey(e.InitFrom)))
                        {
                            texturename = images[e.InitFrom];
                            m.Textures.Add(texturename);
                        }
                    }
                    else if (e.InitFrom == null)
                    {
                        foreach (MaterialNode mat in materials)
                        {
                            if (mat.Effect == "#" + e.Name + "-fx")
                            {
                                texturename = images[mat.InitFrom];
                                m.Textures.Add(texturename);
                            }
                        }

                    }
                    else if (e.Name == material)
                    {
                        m.Materials.Add(e);
                    }
                }


                // Find data source nodes
                XmlNode positionsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "VERTEX");
                XmlNode normalsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "NORMAL");
                XmlNode texcoordsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "TEXCOORD");
                XmlNode tangentsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "TEXTANGENT");

                // Get the data of the sources
                List<float> positions = sources[XmlHelper.GetXmlAttribute(positionsnode, "source").Substring(1)];
                List<float> normals = sources[XmlHelper.GetXmlAttribute(normalsnode, "source").Substring(1)];
                List<float> texcoords = sources[XmlHelper.GetXmlAttribute(texcoordsnode, "source").Substring(1)];
                //List<float> tangents = sources[XmlHelper.GetXmlAttribute(tangentsnode,"source").Substring(1)];

                int positionsStride = sourcesStride[XmlHelper.GetXmlAttribute(positionsnode, "source").Substring(1)];
                int normalsStride = sourcesStride[XmlHelper.GetXmlAttribute(normalsnode, "source").Substring(1)];
                int texcoordsStride = sourcesStride[XmlHelper.GetXmlAttribute(texcoordsnode, "source").Substring(1)];

                // Find the Offsets
                int positionsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(positionsnode, "offset"), NumberFormatInfo.InvariantInfo);
                int normalsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(normalsnode, "offset"), NumberFormatInfo.InvariantInfo);
                int texcoordsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(texcoordsnode, "offset"), NumberFormatInfo.InvariantInfo);
                //int tangentsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(tangentsnode, "offset"), NumberFormatInfo.InvariantInfo);

                // Get the indexlist
                XmlNode p = XmlHelper.GetChildNode(trianglenode, "p");
                int[] pints = StringHelper.ConvertStringToIntArray(p.InnerText);


                int trianglecount = Convert.ToInt32(XmlHelper.GetXmlAttribute(trianglenode, "count"), NumberFormatInfo.InvariantInfo);
                m.NumOfTriangles = trianglecount;



                // The number of ints that form one vertex:
                int vertexcomponentcount = pints.Length / trianglecount / 3;

                // Construct data
                m.Vertices.Clear();
                // Initialize reuseVertexPositions and reverseReuseVertexPositions
                // to make it easier to use them below
                m.ReuseVertexPositions = new int[trianglecount * 3];
                m.ReverseReuseVertexPositions = new List<int>[positions.Count / 3];
                for (int i = 0; i < m.ReverseReuseVertexPositions.Length; i++)
                    m.ReverseReuseVertexPositions[i] = new List<int>();

                // We have to use int indices here because we often have models
                // with more than 64k triangles (even if that gets optimized later).
                for (int i = 0; i < trianglecount * 3; i++)
                {

                    // Position
                    int pos = pints[i * vertexcomponentcount + positionsoffset] * positionsStride;

                    Vector3 position = new Vector3(positions[pos], positions[pos + 1], positions[pos + 2]);


                    //Fixme
                    // Get vertex blending stuff (uses pos too)
                    //Vector3 blendWeights = vertexSkinWeights[pos/3];
                    //Vector3 blendIndices = vertexSkinJoints[pos/3];

                    // Pre-multiply all indices with 3, this way the shader
                    // code gets a little bit simpler and faster
                    //blendIndices = new Vector3(blendIndices.X * 3, blendIndices.Y * 3, blendIndices.Z * 3);

                    Vector3 blendWeights = new Vector3(0.0f, 0.0f, 0.0f);
                    Vector3 blendIndices = new Vector3(0.0f, 0.0f, 0.0f);

                    // Normal
                    int nor = pints[i * vertexcomponentcount + normalsoffset] * normalsStride;
                    Vector3 normal = new Vector3(normals[nor], normals[nor + 1], normals[nor + 2]);


                    //fix me  


                    // Texture Coordinates
                    int tex = pints[i * vertexcomponentcount + texcoordsoffset] * texcoordsStride;
                    float u = texcoords[tex];
                    float v = 1.0f - texcoords[tex + 1]; //mirror tex coord, 

                    //Fixme: tangent support was removed, not sure if we need this
                    // Tangent
                    //int tan = pints[i * vertexcomponentcount + tangentsoffset] * 3;
                    //Vector3 tangent = new Vector3(tangents[tan], tangents[tan + 1], tangents[tan + 2]);
                    Vector3 tangent = new Vector3(1, 1, 1); // dummy vector to satisfy code

                    // Set the vertex
                    m.Vertices.Add(new SkinnedTangentVertex(position, blendWeights, blendIndices, u, v, normal, tangent));

                    // Remember pos for optimizing the vertices later more easily.
                    m.ReuseVertexPositions[i] = pos / 3;
                    m.ReverseReuseVertexPositions[pos / 3].Add(i);
                }

                newmeshes.Add(m);
            }
            return newmeshes;

            #endregion
        }
        #endregion

        #region Load animation
        #region Load animation targets
        /// <summary>
        /// Animation target values to help us loading animations a little.
        /// Thanks to this dictionary we are able to load all float arrays
        /// at once and then use them wherever we need them later to fill in
        /// the animation matrices for every animated bone.
        /// </summary>
        Dictionary<string, float[]> animationTargetValues = new Dictionary<string, float[]>();

        /// <summary>
        /// Load Animation data from a collada file, ignoring timesteps,
        /// interpolation and multiple animations
        /// </summary>
        private void LoadAnimationTargets(XmlNode colladaFile)
        {
            try
            {
                // Get global frame rate
                frameRate = Convert.ToSingle(XmlHelper.GetChildNode(colladaFile, "frame_rate").InnerText);
            }
            catch { } // ignore if that fails

            XmlNode libraryanimation = XmlHelper.GetChildNode(colladaFile, "library_animations");
            if (libraryanimation == null)
                return;

            LoadAnimationHelper(libraryanimation);
        }

        /// <summary>
        /// Load animation helper, goes over all animations in the animationnode,
        /// calls itself recursively for sub-animation-nodes
        /// </summary>
        /// <param name="animationnode">Animationnode</param>
        private void LoadAnimationHelper(XmlNode animationnode)
        {
            // go over all animation elements
            foreach (XmlNode node in animationnode)
            {
                if (node.Name == "animation")
                //not a channel but another animation node
                {
                    LoadAnimationHelper(node);
                    continue;
                } // if (animation.name)

                if (node.Name != "channel")
                    continue;
                string samplername =
                  XmlHelper.GetXmlAttribute(node, "source").Substring(1);
                string targetname = XmlHelper.GetXmlAttribute(node, "target");

                // Find the sampler for the animation values.
                XmlNode sampler = XmlHelper.GetChildNode(animationnode, "id",
                  samplername);

                // Find value xml node
                string valuename = XmlHelper.GetXmlAttribute(
                  XmlHelper.GetChildNode(sampler, "semantic", "OUTPUT"),
                  "source").Substring(1) + "-array";
                XmlNode valuenode = XmlHelper.GetChildNode(animationnode, "id",
                  valuename);

                // Parse values and add to dictionary
                float[] values =
                  StringHelper.ConvertStringToFloatArray(valuenode.InnerText);
                animationTargetValues.Add(targetname, values);

                // Set number of animation we will use in all bones.
                // Leave last animation value out later, but make filling array
                // a little easier (last matrix is just unused then).
                numOfAnimations = values.Length;
                // If these are matrix values, devide by 16!
                if (XmlHelper.GetXmlAttribute(valuenode, "id").Contains("transform"))
                    numOfAnimations /= 16;
            } // foreach (node)
        } // LoadAnimationHelper(animationnode, AnimationTargetValues)
        #endregion

        #region FillBoneAnimations
        /// <summary>
        /// Fill bone animations, called from LoadAnimation after we got all
        /// animationTargetValues.
        /// </summary>
        /// <param name="colladaFile">Collada file</param>
        private void FillBoneAnimations(XmlNode colladaFile)
        {
            foreach (Bone bone in bones)
            {
                // Loads animation data from bone node sid, links them
                // automatically, also generates animation matrices.
                // Note: We only support the transform node here, "RotX", "RotY",
                // etc. will only be used if we don't got baked matrices, but that
                // is catched already when loading the initial bone matrices above.

                // Build sid the way it is used in the collada file.
                string sid = bone.id + "/" + "transform";

                int framecount = 0;
                if (animationTargetValues.ContainsKey(sid))
                    // Transformation contains whole matrix (always 4x4).
                    framecount = animationTargetValues[sid].Length / 16;

                // Expand array and duplicate the initial matrix in case
                // there is no animation data present (often the case).
                for (int i = 0; i < numOfAnimations; i++)
                    bone.animationMatrices.Add(bone.initialMatrix);

                if (framecount > 0)
                {
                    float[] mat = animationTargetValues[sid];
                    // Load all absolute animation matrices. If you want relative
                    // data here you can use the invBoneMatrix (invert initialMatrix),
                    // but this won't be required here because all animations are
                    // already computed. Maybe you need it when doing your own animations.
                    for (int num = 0; num < bone.animationMatrices.Count &&
                      num < framecount; num++)
                    {
                        bone.animationMatrices[num] = FixMatrix(LoadColladaMatrix(mat, num * 16));
                    } // for (num)
                } // if (framecount)
            } // foreach (bone)
        } // FillBoneAnimations(colladaFile)
        #endregion

        #region CalculateAbsoluteBoneMatrices
        /// <summary>
        /// Calculate absolute bone matrices for finalMatrix. Not really required,
        /// but this way we can start using the bones without having to call
        /// UpdateAnimation (maybe we don't want to animate yet).
        /// </summary>
        private void CalculateAbsoluteBoneMatrices()
        {
            foreach (Bone bone in bones)
            {
                // Get absolute matrices and also use them for the initial finalMatrix
                // of each bone, which is used for rendering.
                bone.finalMatrix = bone.GetMatrixRecursively();
            } // foreach (bone)
        } // CalculateAbsoluteBoneMatrices()
        #endregion

        /// <summary>
        /// Load animation
        /// </summary>
        /// <param name="colladaFile">Collada file</param>
        private void LoadAnimation(XmlNode colladaFile)
        {
            // Little helper to load and store all animation values
            // before assigning them to the bones where they are used.
            LoadAnimationTargets(colladaFile);

            // Fill animation matrices in each bone (if used or not, always fill).
            FillBoneAnimations(colladaFile);

            // Calculate all absolute matrices. We only got relative ones in
            // initialMatrix for each bone right now.
            CalculateAbsoluteBoneMatrices();
        } // LoadAnimation(colladaFile)
        #endregion

        #region Update animation
        /// <summary>
        /// Was this animation data already constructed last time we called
        /// UpdateAnimation? Will not optimize much if you render several models
        /// of this type (maybe use a instance class that holds this animation
        /// data and just calls this class for rendering to optimize it further),
        /// but if you just render a single model, it gets a lot faster this way.
        /// </summary>
        private int lastAniMatrixNum = -1;

        /// <summary>
        /// Update animation. Will do nothing if animation stayed the same since
        /// last time we called this method.
        /// </summary>
        /// <param name="renderMatrix">Render matrix just for adding some
        /// offset value to the animation time, remove if you allow moving
        /// objects, this is just for testing!</param>
        internal void UpdateAnimation(Matrix renderMatrix) //Fixme for animation
        {
            // Add some time to the animation depending on the position.
            //int aniMatrixNum = ((int)(100+ renderMatrix.Translation.X / 2.35f + renderMatrix.Translation.Y / 1.05f +	BaseGame.TotalTime * frameRate)) % numOfAnimations;
            //Fixme : engine time
            int aniMatrixNum = ((int)(100 * frameRate)) % numOfAnimations;
            if (aniMatrixNum < 0)
                aniMatrixNum = 0;
            // No need to update if everything stayed the same
            if (aniMatrixNum == lastAniMatrixNum)
                return;
            lastAniMatrixNum = aniMatrixNum;

            foreach (Bone bone in bones)
            {
                // Just assign the final matrix from the animation matrices.
                bone.finalMatrix = bone.animationMatrices[aniMatrixNum];

                // Also use parent matrix if we got one
                // This will always work because all the bones are in order.
                if (bone.parent != null)
                    bone.finalMatrix *= bone.parent.finalMatrix;
            } // foreach

        } // UpdateAnimation()
        #endregion

        #region GetBoneMatrices
        /// <summary>
        /// Get bone matrices for the shader. We have to apply the invBoneSkinMatrix
        /// to each final matrix, which is the recursively created matrix from
        /// all the animation data (see UpdateAnimation).
        /// </summary>
        /// <returns></returns>
        private Matrix[] GetBoneMatrices(Matrix renderMatrix)
        {
            // Update the animation data in case it is not up to date anymore.
            UpdateAnimation(renderMatrix);

            // And get all bone matrices, we support max. 80 (see shader).
            Matrix[] matrices = new Matrix[System.Math.Min(80, bones.Count)];
            for (int num = 0; num < matrices.Length; num++)
                // The matrices are constructed from the invBoneSkinMatrix and
                // the finalMatrix, which holds the recursively added animation matrices
                // and finally we add the render matrix too here.
                matrices[num] = bones[num].invBoneSkinMatrix * bones[num].finalMatrix * renderMatrix;
            return matrices;
        } // GetBoneMatrices()
        #endregion


    }
}
